package com.shs.main;




import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author 晚点再动心
 * @Date 2022/9/17
 * 通过逆波兰式实现表达式混合计算
 * 思路分析：
 * 1.用户输入一个需要计算的表达式（即为中缀式），将中缀式存入到list集合
 * 2.通过将中缀式转换为后缀是进行结果计算
 */
public class Calculator {
    static Stack stack = new Stack();

    /**
     * 对用户输入的字符进行处理
     * 中英文符号之间的替换以及输入的空格做处理
     * @param str 用户输入的字符
     * @return {@link String}
     */
     public static String replaceStr(String str){
        str = str.replace(" ", "");
        str = str.replace("（", "(");
        str = str.replace("）", ")");
        str = str.replace("。", ".");
        String[] strArr = str.split("=");
        return strArr[0];
    }

    /**
     * 将用户输入的表达式（中缀式）变成List
     * 包括用户输入的表达式中小数点和负数的处理
     * @param expression 表达式
     * @return {@link List}<{@link String}>
     */
    public static List<String> stringTolist(String expression) {
        char[] charArray = expression.toCharArray();
        StringBuilder stringBuilder = new StringBuilder();

        ArrayList<String> centerList = new ArrayList<>();
        //遍历用户输入的表达式
        for (int i = 0; i < charArray.length; i++) {
            //循环遍历后判断元素的类型
            //如果是数字就添加到缓冲字符流中，循环的下一个元素还是数字，将其追加到缓冲字符流后面
            if (StringUtils.isNumeric(charArray[i] + "" )|| ".".equals(charArray[i] + "")) {

                //如果是循环的最后一位，直接添加到缓冲字符中
                if ( i == charArray.length - 1 ){
                    centerList.add(charArray[i] + "");
                    //结束本次循环
                    continue;

                }
                stringBuilder.append(charArray[i]);
                continue;

            }else {
                // 如果<0,就不要向list中添加空了
                // 如果>0,说明上一轮循环结束，已经搜集到了完整的数字。添加到list中
                if (stringBuilder.length() > 0){
                    centerList.add(stringBuilder.toString());
                }
                //当用户输入的第一个数字为负数: -9+(3-1)*3+10/2
                if (i == 0 && (charArray[i] + "").equals("-")) {
                    centerList.add("0");
                    centerList.add("-");
                    continue;

                }

                //如果用户输入的表达式中间带负数 9+(-2)*3+10/2
                if((charArray[i] + "").equals("-") && centerList.get(centerList.size() -1).equals("(")){
                    centerList.add("0");
                    centerList.add("-");
                    continue;
                }
                centerList.add(charArray[i] + "");

                //清空内容给下一次循环
                stringBuilder = new StringBuilder();

            }

        }

        return centerList;
    }

    /**
     * 中缀式转后缀式
     * 1.遍历中缀式，遇到数字直接输出（即添加到后缀表达式中）
     * 2.遇到符号就入栈（判断当前符号与栈顶元素的优先级 ：左括号（  > （乘 = 除） >（加 = 减）> 右括号））
     *  2.1.如果符号是右括号），需要匹配左括号，栈顶元素依次出栈（即栈顶元素依次输出到后缀集合中）
     *
     * @param centerList 中缀表达式
     * @return {@link List}<{@link String}>
     */
    public static List<String> centerToEnd(List<String> centerList){

        //后缀表达式集合
        ArrayList<String> endList = new ArrayList<>();
        for (String str : centerList) {
            //如果是数字就添加到后缀集合中
            if (StringUtils.isNumeric(str)){
                endList.add(str);
                continue;
            }
            //判断和栈顶元素的优先级
            if (priority(str)){
                //进栈
                stack.push(str);
            }else {
                //如果当前符号优先级低于栈顶元素，并且当前符号是有括号
                //栈顶元素依次，出栈的元素依次加入到后缀集合中
                if (str.equals(")")){
                    while (!stack.isEmpty()){
                        //栈顶出栈
                        String topValue = stack.pop();
                        //匹配到栈中的左括号结束
                        if (topValue.equals("(")){
                            break;
                        }
                        //栈顶值加入到后缀集合中
                        endList.add(topValue);
                    }
                }else {
                    //当前符号优先级低于栈顶，栈顶符号出栈，当前符号进栈
                    String topValue = stack.pop();
                    //将出栈元素加到后缀集合中
                    endList.add(topValue);
                    stack.push(str);
                }
            }
        }
        //添加最后栈中剩下的元素
        while (!stack.isEmpty()){
            endList.add(stack.pop());
        }

        return endList;

    }

    /**
     * 判断优先级
     *
     * @param str) 中缀式中一个个的符号
     * @return true :当前符号优先级高于栈顶元素，符号进栈
     *         false:当前符号优先级低于栈顶元素，栈顶符号出栈
     *///判断优先级
    public static boolean priority(String str){
        // 空栈，c进栈
        if (stack.isEmpty()) {
            return true;
        }

        // 如果当前符号为右括号，栈顶元素依次出栈，直到匹配到最近的右括号
        if (str.equals(")")) {
            return false;
        }

        // 如果为左括号直接进栈，不匹配优先级
        if (str.equals("(")) {
            return true;
        }

        // 栈顶值
        String topValue = stack.takeOut() + "";

        // 优先级：左括号（  > （乘 = 除） >（加 = 减）> 右括号）
        if (topValue.equals("+") || topValue.equals("-")) {
            if (str.equals("*") || str.equals("/")) {
                return true;
            } else {
                return false;
            }
        } else {
            if (topValue.equals("/") || topValue.equals("*")) {
                if (str.equals("+") || str.equals("-")) {
                    return false;
                } else {
                    return true;
                }
            } else {
                //最后只剩左括号，优先级最高，直接进栈
                return true;
            }
        }
        ////如果栈为空，当前符号直接进栈
        //if (stack.isEmpty()){
        //    return true;
        //}
        ////如果当前符号为右括号，栈顶元素依次出栈，直到匹配到最近的右括号
        //if (str.equals(")")){
        //    return false;
        //}
        ////如果为左括号直接进栈，不匹配优先级
        //if (str.equals("(")){
        //    return true;
        //}
        ////栈顶值
        //String topValue = stack.takeOut() + "";
        //
        ////优先级
        //if (topValue.equals(topValue.equals("+") || topValue.equals("-"))){
        //    if (str.equals("*") || str.equals("/")){
        //        //符号进栈
        //        return true;
        //    }else {
        //        return false;
        //    }
        //}else {
        //    if (topValue.equals("/") || topValue.equals("*")){
        //        if (str.equals("+") || str.equals("-")){
        //            return false;
        //        }else {
        //            return true;
        //        }
        //        //最后只剩左括号，优先级最高，直接进栈
        //    }else {
        //        return true;
        //    }
        //}
    }

    /**
     * 通过后缀式计算表达式结果
     * 1、遍历若遇到数字直接进栈
     * 2.如果是符号就把栈顶元素取出两个与当前符号做计算，得出一个结果，然后在键结果放入栈中
     *     * @param endList 最终名单
     * @return {@link String}
     */
    public static String Calculate(List<String> endList){
        double firstNumber = 0.0;
        double secondNumber = 0.0;
        double result = 0.0;


        //遍历后缀表达式
        for (String str : endList) {

            try {
                //将取出的数字转换为双精度
                Double.parseDouble(str);
                //是数字直接进栈
                stack.push(str);

            }catch (Exception exception){

                //不是数字走catch分支

                //取出两个数字,先出栈的数字在符号的右边
                secondNumber = Double.parseDouble(stack.pop());
                 firstNumber = Double.parseDouble(stack.pop());

                //判断符号进行计算
                if (str.equals("+")){
                    result = firstNumber + secondNumber;
                } else if (str.equals("-")) {
                    result = firstNumber - secondNumber;

                }else if (str.equals("*")) {
                    result = firstNumber * secondNumber;

                }else {
                    result = firstNumber / secondNumber;
                }

                //每次计算出的结果又进栈
                stack.push(result + "");
            }
        }
        //取出栈顶值即为最后运算的结果
        return stack.takeOut();
    }

    
}
